package e3dcs

import (
	"runtime"
	"sync/atomic"
	"time"

	"gitee.com/ameise84/e3dcs/internal/help"
	"gitee.com/ameise84/e3dcs/internal/log"
	"gitee.com/ameise84/e3lock"
	"gitee.com/ameise84/e3pool/go_pool"
	"gitee.com/ameise84/e3pool/obj_pool"
	"gitee.com/ameise84/e3utils/math"
	"gitee.com/ameise84/e3utils/str_conv"
	"gitee.com/ameise84/e3utils/uuid"
)

var (
	_gCID []string
)

func init() {
	growGID(initPollActorNum)
}

func growGID(toN uint64) {
	n := math.CeilToPowerOfTwo(toN)
	baseLen := uint64(len(_gCID))
	if n-baseLen > 1024 {
		n = toN - toN%1024 + 1024
	}
	ar := make([]string, n)
	if baseLen > 0 {
		copy(ar, _gCID)
	}
	xid := uuid.NewXid()
	for i := baseLen; i < n; i++ {
		ar[i] = xid + str_conv.ToString(i)
	}
	_gCID = ar
}

func newBehaviorManager(newActorFunc func(int32) *actorLocal, srv *entity, f ActorAdapterFactory) *behaviorManager {
	return &behaviorManager{
		srv:     srv,
		factory: f,
		actors:  make(map[string]*actorLocal, 2048),
		pool: obj_pool.NewPool[*actorLocal](func() *actorLocal {
			return newActorFunc(srv.opts.msgSize)
		}),
		moveRunner: go_pool.NewGoRunner(nil, "service["+srv.getServiceID()+"] actor manager move runner", go_pool.DefaultOptions().SetSimCount(uint32(runtime.NumCPU()))),
	}
}

type behaviorManager struct {
	srv        *entity
	factory    ActorAdapterFactory
	actors     map[string]*actorLocal
	mu         e3lock.SpinLock
	nextId     atomic.Uint64
	pool       obj_pool.Pool[*actorLocal]
	moveRunner go_pool.GoRunner //用于转移用户
}

func (ts *behaviorManager) moveActors(cb func(*actorLocal) bool) {
	log.GetLogger().Trace("checking move actor...").Object(ts.srv)
	t0 := time.Now()
	moveActors := make(map[string]*actorLocal, 256)
	checkCount := 0
	ts.mu.Lock()
	for k, v := range ts.actors {
		if cb(v) {
			moveActors[k] = v
		}
		checkCount++
	}
	ts.mu.Unlock()
	t1 := time.Now()
	if len(moveActors) > 0 {
		for _, ac := range moveActors {
			_ = ts.moveRunner.AsyncRun(func(args ...any) {
				a := args[0].(*actorLocal)
				a.stop(true)
			}, ac)
		}
		ts.moveRunner.Wait()
	}
	t2 := time.Now()
	check := moveCnt{
		checkCount,
		t1.Sub(t0).Seconds(),
	}
	moved := moveCnt{
		len(moveActors),
		t2.Sub(t1).Seconds(),
	}
	log.GetLogger().Trace("actor move done").Object(ts.srv).Str("check", check.String()).Str("moved", moved.String())
}

func (ts *behaviorManager) stopActors(isMove bool) {
	moveActors := make(map[string]*actorLocal, 256)
	ts.mu.Lock()
	for k, v := range ts.actors {
		moveActors[k] = v
	}
	ts.mu.Unlock()
	for _, ac := range moveActors {
		_ = ts.moveRunner.AsyncRun(func(args ...any) {
			a := args[0].(*actorLocal)
			a.stop(isMove)
		}, ac)
	}
	ts.moveRunner.Wait()
}

func (ts *behaviorManager) stopActor(cid string, isMove bool) {
	ts.mu.Lock()
	if a, ok := ts.actors[cid]; ok {
		_ = ts.moveRunner.AsyncRun(func(...any) {
			a.stop(isMove)
		})
	}
	ts.mu.Unlock()
}

func (ts *behaviorManager) findActor(cid string) (*actorLocal, bool) {
	if cid == "" {
		return nil, false
	}
	ts.mu.Lock()
	defer ts.mu.Unlock()
	_, a, ok := ts._findActor(cid)
	return a, ok
}

func (ts *behaviorManager) findOrStartActor(cid string) (*actorLocal, bool) {
	ts.mu.Lock()
	defer ts.mu.Unlock()

	newCID, a, ok := ts._findActor(cid)
	if ok {
		return a, ok
	}
	//todo 判断如果是关闭actor消息,则退出
	return ts._startActor(newCID)
}

func (ts *behaviorManager) onActorDel(cid string) {
	ts.mu.Lock()
	if a, ok := ts.actors[cid]; ok {
		delete(ts.actors, cid)
		ts.pool.Put(a)
	}
	ts.mu.Unlock()
}

func (ts *behaviorManager) _findActor(cid string) (string, *actorLocal, bool) {
	if cid != "" {
		if v, ok := ts.actors[cid]; ok {
			return cid, v, true
		}
	}
	newCid := cid
	//当hash模式有最大对象数量时,将不是一一绑定,而是通过hash算法,绑定到特定对象上
	srvMode := ts.srv.getBLMode()
	maxCnt := ts.srv.opts.maxActor
	if srvMode == BLModeHash {
		if maxCnt != 0 {
			idx := uint64(0)
			if maxCnt > 1 {
				hashValue := help.Hash64(newCid)
				idx = hashValue % maxCnt
				lenGID := uint64(len(_gCID))
				if idx >= lenGID {
					growGID(idx + 1)
				}
			}
			newCid = _gCID[idx]
		}
	} else {
		if cid == "" { //cid!=""时,是fix方式
			idx := ts.nextId.Add(1) % maxCnt
			lenGID := uint64(len(_gCID))
			if idx >= lenGID {
				growGID(idx + 1)
			}
			newCid = _gCID[idx]
		}
	}
	v, ok := ts.actors[newCid]
	return newCid, v, ok
}

func (ts *behaviorManager) _startActor(cid string) (*actorLocal, bool) {
	a := ts.pool.Get()
	a.init(ts.srv, cid)
	b := ts.factory.TakeActor()

	ts.actors[cid] = a
	a.behavior = b

	return a, a.start(ts.srv.opts.stayTime)
}
